/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <vector>
#include <algorithm>
#include <cmath>

#include "common/pdl.h"
#include "common/pdl_visitors.h"
#include "exceptions/pipeline_exceptions.h"

namespace NAMESPACE{
// ****** PDLDict implementation ***********************************************
PDLLiteral::~PDLLiteral(){}
PDLLiteral::PDLLiteral() : SerializablePDLElement(), value(){}
PDLLiteral::PDLLiteral(double _value) : SerializablePDLElement(), value(_value){}
PDLLiteral::PDLLiteral(const PDLLiteral& literal) : SerializablePDLElement(literal), value(literal.value){}
const PDLLiteral& PDLLiteral::operator=(const PDLLiteral& literal){
    if(this == &literal) return *this;
    SerializablePDLElement::operator=(literal);
    value = literal.value;
    return *this;
}

bool PDLLiteral::validate(double _value) const {
    return value == _value;
}

bool PDLLiteral::validate(const std::string& _value) const {
    return false;
}

const SerializablePDLElement& PDLLiteral::serialize(PDLElementVisitor& vis) const{
    vis.visitPDLLiteral(*this);
    return *this;
}

PDLElement* PDLLiteral::clone() const{
    return new PDLLiteral(*this);
}

SerializablePDLElement* PDLLiteral::cloneSerializable() const{
    return new PDLLiteral(*this);
}

double PDLLiteral::get_value() const{
    return value;
}
// -----------------------------------------------------------------------------

// ****** PDLPair implementation ***********************************************
PDLPair::~PDLPair(){}
PDLPair::PDLPair() : SerializablePDLElement(), label(), value(){}
PDLPair::PDLPair(const std::string& _label, int _value) : SerializablePDLElement(), label(_label), value(_value){}
PDLPair::PDLPair(const PDLPair& element) : SerializablePDLElement(), label(element.label), value(element.value){}
const PDLPair& PDLPair::operator=(const PDLPair& element){
    if(this == &element) return *this;
    SerializablePDLElement::operator=(element);
    label = element.label;
    value = element.value;
    return *this;
}

bool PDLPair::validate(double value) const {
    return false;
}

bool PDLPair::validate(const std::string& _label) const {
    return _label == label;
}

const SerializablePDLElement& PDLPair::serialize(PDLElementVisitor& vis) const{
    vis.visitPDLPair(*this);
    return *this;
}

PDLElement* PDLPair::clone() const{
    return new PDLPair(*this);
}

SerializablePDLElement* PDLPair::cloneSerializable() const{
    return new PDLPair(*this);
}

double PDLPair::get_value() const { return value; }
std::string PDLPair::get_label() const { return label; }
// -----------------------------------------------------------------------------

// ****** PDLRange implementation **********************************************
PDLRange::~PDLRange(){}
PDLRange::PDLRange() : SerializablePDLElement(), from(), to(), step(), scale(){}
PDLRange::PDLRange(double _from, double _to, double _step) : SerializablePDLElement(), from(_from), to(_to), step(_step){
    scale = 1;
    if (from > to) std::swap(from, to);
    double float_part = step - std::floor(step);
    while(float_part < 1 && float_part > 0){
        scale *= 10;
        float_part *= 10;
        float_part = float_part - std::floor(float_part);
    }
    from *= scale;
    to   *= scale;
    step *= scale;
}
PDLRange::PDLRange(const PDLRange& element) : SerializablePDLElement(element), 
                                              from(element.from),
                                              to(element.to),
                                              step(element.step),
                                              scale(element.scale){}
const PDLRange& PDLRange::operator=(const PDLRange& element){
    if(this == &element) return *this;
    SerializablePDLElement::operator=(element);
    from  = element.from;
    to    = element.to;
    step  = element.step;
    scale = element.scale;

    return *this;
}

bool PDLRange::validate(double _value) const {
    double cmp_value = _value * scale;
    return ( (cmp_value >= from) && (cmp_value <= to) && (static_cast<int>(cmp_value - from) % static_cast<int>(step)) == 0 );
}
bool PDLRange::validate(const std::string& label) const {
    return false;
}

const SerializablePDLElement& PDLRange::serialize(PDLElementVisitor& vis) const{
    vis.visitPDLRange(*this);
    return *this;
}
PDLElement* PDLRange::clone() const{
    return new PDLRange(*this);
}
SerializablePDLElement* PDLRange::cloneSerializable() const{
    return new PDLRange(*this);
}

double PDLRange::get_from() const { return from; } 
double PDLRange::get_to() const { return to; } 
double PDLRange::get_step() const { return step; } 
double PDLRange::get_scale() const { return scale; } 
// -----------------------------------------------------------------------------

// ****** PDLComposite implementation ******************************************
PDLComposite::~PDLComposite(){}
PDLComposite::PDLComposite() : SerializablePDLElement(), elements(){}
PDLComposite::PDLComposite(const PDLComposite& element) : SerializablePDLElement(element), elements(element.elements){}
const PDLComposite& PDLComposite::operator=(const PDLComposite& element){
    if(this == &element) return *this;
    SerializablePDLElement::operator=(element);
    elements = element.elements;
    return *this;
}

PDLComposite& PDLComposite::add_element(PDLElement& element){
    if (&element == this) return *this;
    elements.push_back(boost::shared_ptr<PDLElement>(element.clone()));
    return *this;
}

bool PDLComposite::validate(double _value) const {
    std::vector< boost::shared_ptr<PDLElement> >::const_iterator it     = elements.begin(),
                                                                 end_it = elements.end();
    for(; it != end_it; it++){
        if((*it)->validate(_value)) return true;
    }
    return false;
}
bool PDLComposite::validate(const std::string& label) const {
    std::vector< boost::shared_ptr<PDLElement> >::const_iterator it     = elements.begin(),
                                                                 end_it = elements.end();
    for(; it != end_it; it++){
        if(!(*it)->validate(label)) return false;
    }
    return true;
}
const SerializablePDLElement& PDLComposite::serialize(PDLElementVisitor& vis) const {
    vis.visitPDLComposite(*this);
    return *this;
}
PDLElement* PDLComposite::clone() const{
    return new PDLComposite(*this);
}
SerializablePDLElement* PDLComposite::cloneSerializable() const{
    return new PDLComposite(*this);
}

PDLComposite::iterator PDLComposite::begin(){ return PDLCompositeIterator(this); }
PDLComposite::iterator PDLComposite::end() { return PDLCompositeIterator(elements.end()); }
PDLComposite::const_iterator PDLComposite::begin() const { return PDLCompositeConstIterator(this); }
PDLComposite::const_iterator PDLComposite::end() const { return PDLCompositeConstIterator(elements.end()); }
// -----------------------------------------------------------------------------

// ****** PDLParser implementation *********************************************

PDLParser::~PDLParser(){}
PDLParser::PDLParser() : literal_description(),
                         range_description(),
                         pair_description(),
                         element_description(),
                         list_description(){}
PDLParser::PDLParser(const PDLParser& parser) : literal_description(parser.literal_description),
                                                range_description(parser.range_description),
                                                pair_description(parser.pair_description),
                                                element_description(parser.element_description),
                                                list_description(parser.list_description){}
const PDLParser& PDLParser::operator=(const PDLParser& parser){
    if(this == &parser) return *this;
    literal_description = parser.literal_description;
    range_description = parser.range_description;
    pair_description = parser.pair_description;
    element_description = parser.element_description;
    list_description = parser.list_description;
    return *this;
}

// -----------------------------------------------------------------------------

// ****** PDLCompositeIterator implementation **********************************
    
PDLCompositeIterator::PDLCompositeIterator(PDLComposite* _composite) : composite(_composite) {
    iterator = composite->elements.begin();
    end_iterator = composite->elements.end();
}

PDLCompositeIterator::PDLCompositeIterator(const std::vector< boost::shared_ptr<PDLElement> >::iterator& it) : iterator(it) {}

PDLElement& PDLCompositeIterator::operator*() const{ 
    return **iterator;
}
PDLElement* PDLCompositeIterator::operator->() const {  
    return (*iterator).get();
}

PDLCompositeIterator& PDLCompositeIterator::operator++() { 
    ++iterator; 
    return *this;
}
PDLCompositeIterator PDLCompositeIterator::operator++(int i) { 
    PDLCompositeIterator tmp(*this);
    iterator++; 
    return tmp;
}

bool PDLCompositeIterator::operator==(const PDLCompositeIterator& it) const{
    return iterator == it.iterator;
}
bool PDLCompositeIterator::operator!=(const PDLCompositeIterator& it) const{
    return !this->operator==(it);
}
bool PDLCompositeIterator::operator==(const PDLCompositeConstIterator& it) const{
    return iterator == it.iterator;
}
bool PDLCompositeIterator::operator!=(const PDLCompositeConstIterator& it) const{
    return !this->operator==(it);
}

PDLElement* PDLCompositeIterator::next(){
    return (*(iterator++)).get();
}

SerializablePDLElement* PDLCompositeIterator::nextSerializable(){
    PDLElement* el = (*(iterator++)).get();
    SerializablePDLElement* ser_el = dynamic_cast<SerializablePDLElement*>(el);
    if(!ser_el) throw bad_element("PDLElement not serializable");
    
    return ser_el;
}

bool PDLCompositeIterator::has_next() const{
    return iterator != end_iterator;
}
// -----------------------------------------------------------------------------

// ****** PDLCompositeConstIterator implementation **********************************
    
PDLCompositeConstIterator::PDLCompositeConstIterator(const PDLComposite* _composite) : composite(_composite) {
    iterator = composite->elements.begin();
}

PDLCompositeConstIterator::PDLCompositeConstIterator(const std::vector< boost::shared_ptr<PDLElement> >::const_iterator& it) : iterator(it) {}

const PDLElement& PDLCompositeConstIterator::operator*() const{ 
    return **iterator;
}
const PDLElement* PDLCompositeConstIterator::operator->() const {  
    return (*iterator).get();
}

PDLCompositeConstIterator& PDLCompositeConstIterator::operator++() { 
    ++iterator; 
    return *this;
}
PDLCompositeConstIterator PDLCompositeConstIterator::operator++(int i) { 
    PDLCompositeConstIterator tmp(*this);
    iterator++; 
    return tmp;
}

bool PDLCompositeConstIterator::operator==(const PDLCompositeConstIterator& it) const{
    return iterator == it.iterator;
}
bool PDLCompositeConstIterator::operator!=(const PDLCompositeConstIterator& it) const{
    return !this->operator==(it);
}
bool PDLCompositeConstIterator::operator==(const PDLCompositeIterator& it) const{
    return iterator == it.iterator;
}
bool PDLCompositeConstIterator::operator!=(const PDLCompositeIterator& it) const{
    return !this->operator==(it);
}
// -----------------------------------------------------------------------------

}